window.onload = function () {

    selectAction()
}
// let toggle = false
// const btn = document.querySelector('button')
// btn.addEventListener('click', function () {
//     toggle = !toggle
// })

const selectCanvas = document.getElementById('select')
const selectCtx = selectCanvas.getContext('2d')

const grayCanvas = document.getElementById("gray")
const grayCtx = grayCanvas.getContext('2d')

const showCanvas = document.getElementById("show")
const showCtx = showCanvas.getContext('2d')


function selectAction () {
    const img = new Image()
    loadImg(selectCtx, img)

    const mouse = {
        x: 0,
        y: 0
    }

    selectCanvas.addEventListener('mousemove', function (e) {
        mouse.x = e.offsetX
        mouse.y = e.offsetY
    })

    selectCanvas.addEventListener('mousedown', function (e) {
        const imgData = selectCtx.getImageData(0, 0, selectCanvas.width, selectCanvas.height)
        // console.log(imgData)
        for (let i = 0; i < imgData.data.length; i += 4) {
            let [r, g, b] = [imgData.data[i], imgData.data[i + 1], imgData.data[i + 2]]
            let gray = calculateGrayValue(r, g, b)
            // console.log(imgData.height, imgData.width, i, r, g, b, gray)
            imgData.data[i] = gray
            imgData.data[i + 1] = gray
            imgData.data[i + 2] = gray
        }
        grayCtx.putImageData(imgData, 0, 0)
        showAction()
    })

    anim(selectCtx, img, mouse)
}

//计算图像的灰度值,公式为：Gray = R*0.299 + G*0.587 + B*0.114
function calculateGrayValue (rValue, gValue, bValue) {
    // return parseInt(rValue * 0.299 + gValue * 0.587 + bValue * 0.114)
    return (rValue + gValue + bValue) / 3
}

function anim (ctx, img, mouse) {
    ctx.clearRect(0, 0, 400, 400)
    ctx.drawImage(img, 0, 0, 400, 400)
    // drawCircle(ctx, mouse.x, mouse.y, 150)
    requestAnimationFrame(function () {
        anim(ctx, img, mouse)
    })
}

function showAction () {

    const [x0, y0, r] = [200, 200, 199]
    // drawCircle(showCtx, x0, y0, r)
    const dotNum = 200
    let list = new CycleList()
    for (let i = 0; i < dotNum; ++i) {
        let x = x0 + r * Math.cos(i * (360 / dotNum) * Math.PI / 180)
        let y = y0 + r * Math.sin(i * (360 / dotNum) * Math.PI / 180)
        drawDot(showCtx, x, y, 1)
        list.push(new Node({ x, y }))
    }
    // list.display()
    path = [] // 记录走过的路径，下次排除该集合中的路径
    const imgData = grayCtx.getImageData(0, 0, grayCanvas.width, grayCanvas.height)
    // showCtx.putImageData(imgData, 0, 0)

    let index = Math.ceil(Math.random() * list.index)
    const node = list.findNodeByIndex(index)
    let { minWeightNode, minWeightIndex, dots } = list.findNodeByMinWeight(index, imgData, path)
    // console.log(node.toString(), minWeightNode.toString(), index)

    showCtx.lineWidth = 0.5
    showCtx.strokeStyle = 'rgba(190, 0, 0, 0.2)'
    let count = 0
    showAnim(count, imgData, list, index, node, minWeightIndex, minWeightNode, dots)


}

function showAnim (count, imgData, list, index, node, minWeightIndex, minWeightNode, dots) {
    if (count > 2000) {
        console.log('paint end')
        return
    }
    count++
    showCtx.beginPath()
    showCtx.moveTo(node.x, node.y)
    showCtx.lineTo(minWeightNode.x, minWeightNode.y)
    showCtx.stroke()
    updateWeight(imgData, dots)
    path.push(index * 1000 + minWeightIndex)
    path.push(minWeightIndex * 1000 + index)
    index = minWeightIndex
    node = minWeightNode
    result = list.findNodeByMinWeight(index, imgData, path)
    minWeightNode = result.minWeightNode
    minWeightIndex = result.minWeightIndex
    dots = result.dots
    // console.log(i, index, minWeightNode.toString(), path)
    showCtx.closePath()

    requestAnimationFrame(function () {
        showAnim(count, imgData, list, index, node, minWeightIndex, minWeightNode, dots)
    })
}

function updateWeight (imgData, dots) {
    const addWeight = 40
    dots.forEach(function (item) {
        const idx = item.y * imgData.width * 4 + item.x * 4
        const gray = imgData.data[idx]
        imgData.data[idx] += gray + addWeight
        imgData.data[idx + 1] += gray + addWeight
        imgData.data[idx + 2] += gray + addWeight
        grayCtx.putImageData(imgData, 0, 0)
    })
}

function calWeight (imgData, point1, point2) {
    const [startX, startY, endX, endY] = [point1.x, point1.y, point2.x, point2.y]
    const dots = []

    k = (endY - startY) / (endX - startX)
    b = startY - k * startX

    const direct = Math.abs(endX - startX) >= Math.abs(endY - startY)

    let growth = 1,
        count = 0,
        sumWeight = 0
    if (direct) {
        if (startX > endX) {
            growth = -1
        }
        let i = parseInt(startX)

        while (i !== parseInt(endX)) {
            const x = i,
                y = parseInt(k * x + b)
            dots.push({ x, y })
            const idx = y * imgData.width * 4 + x * 4
            sumWeight += imgData.data[idx]
            i += growth
            count++
            // drawCircle(showCtx, x, y, 1)
            // console.log(startX, startY, endX, endY, x, y, i)
        }
    } else {
        if (startY > endY) {
            growth = -1
        }
        let i = parseInt(startY)
        while (i !== parseInt(endY)) {
            const y = i,
                x = parseInt((y - b) / k)
            dots.push({ x, y })
            const idx = y * imgData.width * 4 + x * 4
            sumWeight += imgData.data[idx]
            i += growth
            count++
            // drawCircle(showCtx, x, y, 1)
            // console.log(startX, endX, x, y, i)
        }
    }
    return { weight: sumWeight / count, dots }

}

function loadImg (ctx, img) {
    const file = document.querySelector('input[type=file]')
    file.addEventListener('change', function () {
        const fr = new FileReader()
        fr.onload = function () {
            img.src = this.result
            img.onload = function () {
                ctx.drawImage(img, 0, 0, 400, 400)
                showCtx.clearRect(0, 0, 400, 400)
            }
        }
        fr.readAsDataURL(this.files[0])
    })
}

function drawCircle (ctx, x, y, r) {
    ctx.beginPath()
    ctx.arc(x, y, r, 0, 2 * Math.PI)
    ctx.closePath()
    ctx.fillStyle = 'rgba(255, 255, 0, 0.2)'
    ctx.stroke()
}

function drawDot (ctx, x, y, r) {
    ctx.beginPath()
    ctx.moveTo(x, y)
    ctx.arc(x, y, r, 0, 2 * Math.PI)
    ctx.fill()
}

class Node {
    constructor(point) {
        this.x = point.x
        this.y = point.y
        this.prev = null
        this.next = null
    }

    toString () {
        return `Point[${this.x}, ${this.y}]`
    }
}

class CycleList {
    constructor() {
        this.index = 0
        this.head = new Node({ x: 0, y: 0 })
        this.head.prev = this.head
        this.head.next = this.head
    }

    push (node) {
        this.head.prev.next = node
        node.prev = this.head.prev
        node.next = this.head
        this.head.prev = node
        this.index += 1
    }

    findNodeByMinWeight (index, imgData, path) {
        if (index <= 0 || index > this.index) {
            return undefined
        }

        const prevIndex = index - 1 <= 0 ? this.index : index - 1
        const nextIndex = index + 1 > this.index ? 1 : index + 1
        // console.log(prevIndex, nextIndex)

        let minWeightIndex = -1,
            currentNode = this.findNodeByIndex(index),
            minWeightNode,
            minWeight = 256,
            dots = []
        for (let i = 1; i <= this.index; ++i) {
            if (i === prevIndex || i === nextIndex || i === index) continue
            // const path1 = i * 1000 + index, path2 = index * 1000 + i
            // console.log(path1, path1 in path, path2, path2 in path, path)
            if (path.includes(i * 1000 + index) || path.includes(index * 1000 + i)) continue

            const findNode = this.findNodeByIndex(i),
                result = calWeight(imgData, currentNode, findNode)
            // console.log(weight, minWeight)
            if (result.weight < minWeight) {
                minWeight = result.weight
                minWeightIndex = i
                minWeightNode = findNode
                dots = result.dots
            }
        }
        // console.log(minWeightNode, minWeightIndex)
        return { minWeightNode, minWeightIndex, dots }
    }

    findNodeByIndex (index) {
        if (index <= 0 || index > this.index) {
            return undefined
        }

        let i = 0
        let current = this.head
        while (current.next !== this.head && i !== index) {
            current = current.next
            i++
        }
        return current
    }

    findPrev (node) {
        return node.prev
    }

    findNext (node) {
        return node.next
    }

    display () {
        let current = this.head
        while (current.next !== this.head) {
            current = current.next
            console.log(current.toString())
        }
    }
}
